#!/usr/bin/env python
# coding: utf-8

# # 基于PaddleX的人体口罩穿戴检测
# 
# 基于PaddleX的YOLOv3对人体口罩穿戴进行检测，包括数据集划分、数据加载和预处理、模型训练等步骤。
# 
# # 一、项目背景
# 
# 由于疫情爆发，再加上人们安全卫生意识的提高，去一些人群密集、环境较为密闭的环境时都要求我们穿戴口罩，并且需要正确穿戴口罩。人工进行不间断的检查需要耗费大量的人力，本项目基于PaddleX进行人体口罩穿戴检测，实现实时检测人们的口罩穿戴情况和是否正确佩戴口罩，减少了人力，为各场所提供了较大的便利。
# 
# ![](https://ai-studio-static-online.cdn.bcebos.com/75d6c9b07c8a4f4ead3ee4e522caff8f1c51be42dacb45dea8c859066b8993bc)
# 
# ![](https://ai-studio-static-online.cdn.bcebos.com/bd8a866f8c6943bb98aedf3b19a2f467ba4ca21451f942f5bed4b8e58e838cc7)
# 
# ![](https://ai-studio-static-online.cdn.bcebos.com/10b7bdbf90b54e39911dc97bcbc8c4297a04b9a58ad1403bb19961b0923b31be)
# 
# 
# # 二、数据集简介
# 
# 本基线系统使用的数据格式是PascalVOC格式，为了进行训练，还需要将数据划分为70%训练集，20%验证集和10%的测试集。划分之前首先需要安装PaddleX。
# 
# ## 1.数据集加载
# 
# ```python
# # 安装PaddleX
# !pip install paddlex
# 
# # 解压数据集
# !unzip -oq /home/aistudio/data/data104243/FaceMask.zip
# 
# # 划分数据集
# !paddlex --split_dataset --format VOC --dataset_dir objDataset/facemask --val_value 0.2 --test_value 0.1
# ```
# 一共有853个样本数据，此处可细分，如下所示：
# 
# ```python
# Dataset Split Done.
# Train samples: 598
# Eval samples: 170
# Test samples: 85
# ```
# 
# ## 2.数据预处理
# 
# 在训练模型之前，对目标检测任务的数据进行操作，从而提升模型效果
# ```python
# from paddlex.det import transforms
# 
# # 定义训练和验证时的transforms
# # API说明 https://paddlex.readthedocs.io/zh_CN/develop/apis/transforms/det_transforms.html
# train_transforms = transforms.Compose([
#     transforms.RandomCrop(aspect_ratio=[.5, 2.], thresholds=[.0, .1, .3, .5, .7, .9], scaling=[.3, 1.], num_attempts=50, allow_no_crop=True, cover_all_box=False),
#     transforms.Resize(target_size=60, interp='RANDOM'),
#     transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, contrast_range=0.5, contrast_prob=0.5, saturation_range=0.5, saturation_prob=0.5, hue_range=18, hue_prob=0.5),
#     transforms.RandomHorizontalFlip(),
#     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], min_val=[0., 0., 0.], max_val=[255., 255., 255.]),
# ])
# 
# eval_transforms = transforms.Compose([
#     transforms.Resize(target_size=60, interp='CUBIC'),
#     transforms.RandomDistort(brightness_range=0.5, brightness_prob=0.5, contrast_range=0.5, contrast_prob=0.5, saturation_range=0.5, saturation_prob=0.5, hue_range=18, hue_prob=0.5),
#     transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225], min_val=[0., 0., 0.], max_val=[255., 255., 255.]),
# ])
# 
# import paddlex as pdx
# 
# # 定义训练和验证所用的数据集
# # 读取PascalVOC格式的检测数据集，并对样本进行相应的处理
# train_dataset = pdx.datasets.VOCDetection(
#     data_dir='objDataset/facemask',
#     file_list='objDataset/facemask/train_list.txt',
#     label_list='objDataset/facemask/labels.txt',
#     transforms=train_transforms,
#     shuffle=True)
# 
# eval_dataset = pdx.datasets.VOCDetection(
#     data_dir='objDataset/facemask',
#     file_list='objDataset/facemask/val_list.txt',
#     label_list='objDataset/facemask/labels.txt',
#     transforms=eval_transforms)
# 
# print('训练集样本量: {}，验证集样本量: {}'.format(len(train_dataset), len(eval_dataset)))
# ```
# ```python
# Dataset Split Done.
# Train samples: 598
# Eval samples: 170
# Test samples: 85
# ```
# 训练集样本量: 598，验证集样本量: 170
# 
# 
# ## 3.数据集查看
# 
# 
# ```python
# print('图片：')
# print(type(train_dataset[0][0]))
# print(train_dataset[0][0])
# print('标签：')
# print(type(train_dataset[0][1]))
# print(train_dataset[0][1])
# 
# # 可视化展示
# plt.figure()
# plt.imshow(train_dataset[0][0].reshape([28,28]), cmap=plt.cm.binary)
# plt.show()
# 
# ```
# 
# 
# # 三、模型选择和调参
# 
# 
# ## 1.模型选择
# 
# 本系统以骨干网络为MobileNetV1的YOLOv3算法为模型。
# 
# ```python
# # 初始化模型
# model = pdx.det.YOLOv3(num_classes=len(train_dataset.labels), backbone='MobileNetV1')
# ```
# 
# ## 2.模型训练
# 
# 调整训练相关参数,包括训练轮数等参数
# 
# ```python
# # 配置优化器、损失函数、评估指标
# model.prepare(paddle.optimizer.Adam(learning_rate=0.001, parameters=network.parameters()),
#               paddle.nn.CrossEntropyLoss(),
#               paddle.metric.Accuracy())
# # 模型训练              
# model.train(
#     num_epochs=270,
#     train_dataset=train_dataset,
#     train_batch_size=8,
#     eval_dataset=eval_dataset,
#     learning_rate=0.000125,
#     lr_decay_epochs=[210, 240],
#     save_interval_epochs=20,
#     save_dir='output/yolov3_mobilenetv1')             
# ```
# 
# ```python
# 2021-08-12 22:03:03 [INFO]	Start to evaluating(total_samples=170, total_steps=22)...
# 100%|██████████| 22/22 [00:05<00:00,  3.68it/s]
# 2021-08-12 22:03:09 [INFO]	[EVAL] Finished, Epoch=270, bbox_map=0.757576 .
# 2021-08-12 22:03:10 [INFO]	Model saved in output/yolov3_mobilenetv1/epoch_270.
# 2021-08-12 22:03:10 [INFO]	Current evaluated best model in eval_dataset is epoch_40, bbox_map=3.0303030303030303
# ```
# 程序自动记录了bbox_map大的情况相应的参数，并作为best_model（存在于/home/aistudio/work/PaddleDetection/output/yolov3_MobileNetV1/best_model）
# 
# ## 3.模型预测
# 
# 采用paadlex.visualize_detection来进行单张或少量多张图片的预测。
# 
# ```python
# import glob
# import numpy as np
# import threading
# import time
# import random
# import os
# import base64
# import cv2
# import json
# import paddlex as pdx
# from PIL import Image
# 
# # 传入待预测图片
# image_name = 'objDataset/facemask/JPEGImages/maksssksksss110.png'
# # 模型保存位置
# model = pdx.load_model('output/yolov3_mobilenetv1/best_model')
# 
# img = cv2.imread(image_name)
# result = model.predict(img)
# 
# keep_results = []
# areas = []
# f = open('./output/yolov3_mobilenetv1/result.txt', 'a')
# count = 0
# for dt in np.array(result):
#     cname, bbox, score = dt['category'], dt['bbox'], dt['score']
#     if score < 0.5:
#         continue
#     keep_results.append(dt)
#     # 检测到未佩戴口罩或未正确佩戴口罩的目标，计数加1
#     if cname == 'without_mask': 
#         count += 1
#     if cname == 'mask_weared_incorrect': 
#         count += 1
#     f.write(str(dt) + '\n')
#     f.write('\n')
#     areas.append(bbox[2] * bbox[3])
# areas = np.asarray(areas)
# sorted_idxs = np.argsort(-areas).tolist()
# keep_results = [keep_results[k]
#                 for k in sorted_idxs] if len(keep_results) > 0 else []
# print(keep_results)
# print(count)
# f.write("未佩戴或未正确佩戴口罩的总数为: " + str(int(count)))
# f.close()
# 
# # 定义预测结果输入参数
# pdx.det.visualize(
#     image_name, result, threshold=0.5, save_dir='./output/yolov3_mobilenet')
# ```
# 
# # 四、效果展示
# 
# 该项目已在baseline里跑通整个模型训练，最后的模型预测程序运行完没有任何问题，但不知为何最后的图片里没有结果显示。
# 
# 
# # 五、总结与升华
# 
# 1、制作数据集的时候要耐心，文件夹的格式还有文件名要注意一般有约定
# 
# 2、paddle上手还是很容易的，后面有时间争取自己做一个原创项目
# 
# 3、最后模型预测出了一些问题，问了有些大佬结果还是不能解决，有点可惜没有看到最后显示的结果
# 
# 
# # 个人简介
# 
# ![](https://ai-studio-static-online.cdn.bcebos.com/507acfa728ee4199a428d16ebd7874218f1560968dc241ed94eaf8ba146eeb2e)
# 
